/* -*- c++ -*-
   asmparser.cc - LLVMIR Assembler parser
   Copyright (C) 2011 Matthew Wahab <mwb.cde@gmail.com>

   This file is part of GCC-LLVMIR.

   GCC-LLVMIR is free software; you can redistribute it and/or modify it under
   the terms of the GNU General Public License as published by the Free
   Software Foundation; either version 3, or (at your option) any later
   version.

   GCC-LLVMIR is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   for more details.

   You should have received a copy of the GNU General Public License
   along with GCC-LLVMIR; see the file COPYING3.  If not see
   <http://www.gnu.org/licenses/>.  */

#include "llvmir.h"
#include "asmparser.h"

namespace llvmir
{
  /*
   * LLVM Asm parser
   */

  /* Open and parse a bit-code file, returning the module or 0 on
     failure. (In external.cc) */
  llvm::Module*
  parse_asm_file(llvm::LLVMContext* llvm_context,
                 llvm::SMDiagnostic* llvm_diagnostic,
                 const std::string& filename);


  /* Toplevel parse method. Reads an LLVM asm or bitcode file. */
  llvm::Module*
  AsmParser::parse_file(Context* context,
                        const char* filename)
  {
    llvm::SMDiagnostic llvm_diagnostic;
    llvm::Module* llvm_module = 0;
    std::string name_str(filename);
    
    // Read and parse the file.
    llvm_module = llvmir::parse_asm_file(&(context->llvm_context),
                                         &(llvm_diagnostic),
                                         name_str);

    if (!llvm_module)
      fatal_error("failed to read %s: %m", filename);

    return llvm_module;
  }

#ifdef LLVMIR_USE_CLANG
  /*
   * Clang parser
   */

  /* Open and parse a file with clang. (In external.cc) */
  llvm::Module*
  clang_parse_file(llvm::LLVMContext* llvm_context,
                   llvm::SMDiagnostic* llvm_diagnostic,
                   const StringSeq& arg_seq,
                   const std::string& filename,
                   const std::string& temp_filename);

  /* Destructor */
  ClangParser::~ClangParser()
  { /* skip */ }


    /* Test whether the parser accepts input from files with a given
       name.  Used to determine whether the parser should be selected
       for an input file. 

       Accepts files with suffix: ".c.ll", ".h.ll".
    */
  bool 
  ClangParser::accepts_filename(Context* context ATTRIBUTE_UNUSED, 
                                const std::string& filename)
  {
    const static std::string suffix[] =
      { ".h.ll", ".c.ll", "" };
    const size_t filename_len = filename.length();

    if (!filename_len)
      return false;

    for (unsigned idx = 0;
         suffix[idx].length() > 0;
         idx += 1)
      {
        const size_t suffix_len = suffix[idx].length();

        if (filename_len < suffix_len)
          continue;

        std::string file_suffix = filename.substr(filename_len - suffix_len,
                                                  suffix_len);
        if (file_suffix.compare(suffix[idx]) == 0)
          return true;
      }

    return false;
  }

  /* Process options, called after all command line arguments have
     been processed. */
  bool
  ClangParser::process_options(Options* options)
  { 
    cmdline_opts.clear();

    // Add the include directories
    for (StringSeq::const_iterator
           itr = options->include_dirs.begin(),
           end = options->include_dirs.end();
         itr != end;
         ++itr)
      {
        const std::string itr_str = *itr;
        cmdline_opts.push_back("-I");
        cmdline_opts.push_back(itr_str);
      }
    // Add other options.
    for (StringSeq::const_iterator
           itr = options->parser_opts.begin(),
           end = options->parser_opts.end();
         itr != end;
         ++itr)
      {
        const std::string itr_str = *itr;
        cmdline_opts.push_back(itr_str);
      }

    return true;
  }

  /* Toplevel parse method. Call Clang on filename. */
  llvm::Module*
  ClangParser::parse_file(Context* context,
                          const char* filename)
  {
    llvm::SMDiagnostic llvm_diagnostic;
    llvm::Module* llvm_module = 0;
    char* temp_name = make_temp_file("llvmir");

    // Read and parse the file.
    llvm_module = llvmir::clang_parse_file(&(context->llvm_context),
                                           &(llvm_diagnostic),
                                           this->cmdline_opts,
                                           filename,
                                           temp_name);

    if (!llvm_module)
      fatal_error("failed to read %s: %m", filename);

    // Delete temporary file.
    unlink_if_ordinary(temp_name);
    free(temp_name); 
    temp_name = 0;

    return llvm_module;
  }
#endif // LLVMIR_USE_CLANG

} // End namespace llvmir
